package com.xmlit.project.engine.marshal;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java1.util.regex.Matcher;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import java1.util.regex.*;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.xmlit.project.engine.struct.Struct;
import com.xmlit.project.engine.struct.StructChoice;
import com.xmlit.project.engine.struct.StructSequence;
import com.xmlit.project.engine.struct.StructSimple;
import com.xmlit.project.engine.struct.impl.CalcContext;

public class UnmarshalImpl implements Unmarshal {

	static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	static DocumentBuilder db = null;// dbf.newDocumentBuilder();
	static {
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public Document unmarshal(String message, Struct struct) {
		Document doc = db.newDocument();
		String regex = struct.calcRegex();
		// System.out.println(regex);
		Element element = doUnmarshal(null, message, struct, doc, regex,
				new UnmarshalContext(0));
		doc.appendChild(element);

		return doc;
	}

	public Element doUnmarshal(Element element, String message, Struct struct,
			Document doc, String regex, UnmarshalContext context) {
		if (StructPatternHelper.patternMapping.get(struct) == null) {
			StructPatternHelper.patternMapping.put(struct,
					Pattern.compile(regex));
		}

		Pattern patt = StructPatternHelper.patternMapping.get(struct);
		Matcher matches = patt.matcher(message);
		matches.lookingAt();
		int lastPossition = getLast(matches);
		/*List<String> matchesl = new ArrayList<String>();
		if (matches.matches()) {
			for (int i = 0; i <= matches.groupCount(); i++) {
				matchesl.add(matches.group(i));
				// System.out.println(m1.group(i));
			}
		}
*/
		try {
			return createElement(element, struct, doc, matches, context);
		} catch (Exception e) {

			throw new RuntimeException("Last possition is:" + lastPossition,
					e);
		}

	}

	private int getLast(Matcher matches) {
		try {
			Field f = Matcher.class.getDeclaredField("last");
			f.setAccessible(true);
			return f.getInt(matches);
		} catch (Exception e) {
			return 0;
		}
		//return 0;
	}

	private Element createElement(Element element, Struct struct, Document doc,
			Matcher matches, UnmarshalContext context) {
		if (struct.getMaxOccurrences() != 1
				&& matches.group(struct.getRegularExpressionIndex()
						- context.baseIndex) != null
				&& struct.getRegularExpressionIndexWithoutOccur() >= context.baseIndex
				&& !matches.group(
						struct.getRegularExpressionIndexWithoutOccur()
								- context.baseIndex).equals(
						matches.group(struct.getRegularExpressionIndex()
								- context.baseIndex))) {

			String field = matches.group(struct
					.getRegularExpressionIndexWithoutOccur()
					- context.baseIndex);
			if (StructPatternHelper.patternPureMapping.get(struct) == null) {
				StructPatternHelper.patternPureMapping.put(struct,
						Pattern.compile(struct.getRegularExpressionPure()));
			}
			Pattern datePatt = StructPatternHelper.patternPureMapping
					.get(struct);
			Matcher m = datePatt.matcher(field);
			while (m.find()) {
				// struct.setRegularExpression(struct.getRegularExpressionPure());
				String group = m.group();
				if (group == null || "".equals(group))
					continue;
				Element childElement = doUnmarshal(element, group, struct, doc,
						struct.getRegularExpressionPure(),
						new UnmarshalContext(
								struct.getRegularExpressionIndex() - 1));
				if (childElement != null)
					element.appendChild(childElement);
			}

			return element;
		} else {
			if (struct instanceof StructSequence) {
				return createElementSequence(element, (StructSequence) struct,
						doc, matches, context);
			} else if (struct instanceof StructChoice) {
				return createElementChoice(element, (StructChoice) struct, doc,
						matches, context);
			} else {
				return createElementSimple(element, (StructSimple) struct, doc,
						matches, context);
			}

		}

	}

	private Element createElementSimple(Element parent, StructSimple struct,
			Document doc, Matcher matches, UnmarshalContext context) {
		String val = matches.group(struct.getRegularExpressionIndex()
				- context.baseIndex);
		if (val != null) {
			Element result = doc.createElement(struct.getName());
			result.setTextContent(val);
			parent.appendChild(result);
			return result;
		}

		return null;
	}

	private Element createElementChoice(Element parent, Struct struct,
			Document doc, Matcher matches, UnmarshalContext context) {

		String val = matches.group(struct.getRegularExpressionIndex()
				- context.baseIndex);
		if (val != null && !"".equals(val)) {
			Element result = doc.createElement(struct.getName());
			for (Struct child : struct.getChildren()) {
				createElement(result, child, doc, matches, context);
			}

			if (parent != null)
				parent.appendChild(result);
			return result;
		}

		return null;

	}

	private Element createElementSequence(Element element, Struct struct,
			Document doc, Matcher matches, UnmarshalContext context) {
		return createElementChoice(element, struct, doc, matches, context);
	}

}
